<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>小球连线</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    canvas {
      display: block;
      margin: 20px auto 0;
      border: 1px solid #ccc;
    }
  </style>
</head>

<body>
  <canvas id="myCanvas"></canvas>
  <script>
    const canvas = document.getElementById("myCanvas");
    const ctx = canvas.getContext("2d");

    // 维护小球数组
    const ballArr = [];

    // console.log(document.documentElement.clientWidth);

    // 设置画布尺寸
    canvas.width = document.documentElement.clientWidth - 50;
    canvas.height = document.documentElement.clientHeight - 50;

    function Ball() {
      this.x = parseInt(Math.random() * canvas.width);
      this.y = parseInt(Math.random() * canvas.height);
      this.r = 15;
      this.color = getRandomColor();
      // 设置行进方向
      this.dx = parseInt(Math.random() * 10) - 5 || 1;
      this.dy = parseInt(Math.random() * 10) - 5 || 1;
      ballArr.push(this);
      // 记录下当前的数组下标: 用于连线时两个小球只产生一个条线
      this.index = ballArr.length - 1;
    }

    Ball.prototype.render = function () {
      ctx.beginPath();
      // 透明度
      ctx.globalAlpha = 0.8;
      // 画小球
      ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
      ctx.fillStyle = this.color;
      ctx.fill();

      // 画线逻辑
      // 1. 每一个小球都会执行这一个render函数
      // 2. 每一个小球都有自己的x坐标与y坐标
      // 3. 判断当前小球与其它小球的x坐标与y坐标的差值(绝对值)是否小于100(某一个我们自己定义的坐标值)
      // 4. 若小于之后将两个小球坐标位置进行连线
      const crossBorderV = 200;
      for (let i = this.index; i < ballArr.length; i++) {
        // console.log("可以连线了", crossBorderV, Math.abs(ballArr[i].x - this.x), Math.abs(ballArr[i].y - this.y));
        if (Math.abs(ballArr[i].x - this.x) < crossBorderV && Math.abs(ballArr[i].y - this.y) < crossBorderV) {
          // 创建一个路径
          ctx.beginPath();
          // 线的透明度
          ctx.globalAlpha = 10 / Math.sqrt(Math.pow(ballArr[i].x - this.x, 2) + Math.pow(ballArr[i].y - this.y, 2))
          // 绘制开始路径点
          ctx.moveTo(this.x, this.y);
          // 行进绘制路径点
          ctx.lineTo(ballArr[i].x, ballArr[i].y);
          // 封闭路径
          ctx.closePath();
          ctx.strokeStyle = "#000";
          ctx.stroke();
        }
      }
    }

    Ball.prototype.update = function () {
      this.x += this.dx;
      this.y += this.dy;
      // console.log(this.x);
      // TODO: 判断小球是否走出屏幕了
      if (this.x < this.r || this.x > canvas.width) this.dx = -this.dx;
      if (this.y < this.r || this.y > canvas.height) this.dy = -this.dy;
    }

    for (let i = 0; i < 45; i++) {
      new Ball();
    }

    // 定时器动画
    setInterval(() => {
      // console.log(ballArr);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      for (let i = 0; i < ballArr.length; i++) {
        ballArr[i].update();
        ballArr[i].render();
      }
    }, 15);

    // 随机颜色
    function getRandomColor() {
      const allType = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f";
      const allTypeArr = allType.split(",");
      let color = "#";
      // 十六进制 6位
      for (let i = 0; i < 6; i++) {
        // console.log(ballArr.length);
        let random = parseInt(Math.random() * allTypeArr.length);
        color += allTypeArr[random];
      }
      // console.log("随机颜色", color);
      return color;
    }
  </script>
</body>

</html>